package com.google.wavesurferrobot.textmining;

/*
*************************************************************************************************
* File:         Document.java                                                                   *
* Usage:        Document is used to hold the TF space of a text document                        *
*************************************************************************************************
* IDE/Compiler: For the development needs we used NetBeans 5.0 and JRE 1.5.0_06 on WinXP Home   *
*************************************************************************************************
* License:      (LGPL) GNU Lesser General Public License                                        *
*               http://www.opensource.org/licenses/lgpl-license.php                             *
*************************************************************************************************
*         Originally written by George Tsatsaros (gbt@aueb.gr)                                  *      
*               Author         :  Panayiotis Papadopoulos (3010010)                             *
*               Website        :  http://dias.aueb.gr/~p3010010/                                *
*               E-mail         :  papado@freemail.gr                                            *
*                                 p3010010@dias.aueb.gr                                         *
*               MSN messenger  :  pap5@hotmail.com                                              *
*               Skype          :  papOnSlayer                                                   *
*                                                                                               *
* Contact:  Feel free to contact with me for any question/suggestion using the email(s) above   *
*************************************************************************************************
*/

import java.util.Comparator;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Arrays;
import java.util.Map;

public class Document
{
    private String docID;
    private Hashtable<String,Integer> TF;
    private Hashtable normTF;
    private Hashtable coordinates;
    private Hashtable qualityMeasures;
    private int SN;

    //Holds ordered tokens
    private Vector orderedVector;


    public Document(String anID)
    {
        docID = anID;
        TF = new Hashtable();
        normTF = new Hashtable();
        coordinates = new Hashtable();
        qualityMeasures = new Hashtable();
        SN = 0;
        orderedVector = new Vector();
    }

    public void addTokenToTF(String aToken)
    {
        if (tokenExists(aToken))
        {
            Integer anInteger = (Integer) TF.get(aToken);
            TF.put(aToken, new Integer(anInteger.intValue() + 1));
            SN += 1;
        }
        else
        {
            TF.put(new String(aToken), new Integer(1));
            SN += 1;
        }
    }


    public boolean tokenExists(String aToken)
    {
        return TF.containsKey(aToken);
    }


    public String getID()
    {
        return docID;
    }

    public int getSN()
    {
        return SN;
    }

    public Hashtable<String,Integer> getTF()
    {
        return TF;
    }

    public Integer getTFFor(String aToken)
    {
        return (Integer) TF.get(aToken);
    }

    public void computeNormTF()
    {
        String tmpString = new String("");
        Integer tmpInteger;
        double tmpDouble;
        Enumeration en = TF.keys();
        while (en.hasMoreElements())
        {
            tmpString = (String) en.nextElement();
            tmpInteger = (Integer) TF.get(tmpString);
            tmpDouble = (double) tmpInteger.intValue() / (double) SN;
            normTF.put(new String(tmpString), new Double(tmpDouble));
        }
    }
    
    public double getQuality(String aToken)
    {
        Double qm = (Double)qualityMeasures.get(aToken);
        return qm.doubleValue();
    }


    public Hashtable getNormTF()
    {
       return normTF;
    }

    public Double getNormTFFor(String aToken)
    {
        return (Double) normTF.get(aToken);
    }

    public Hashtable getCoordinates()
    {
       return coordinates;
    }

    public Double getCoordinateFor(String aToken)
    {
       return (Double) coordinates.get(aToken);
    }

    public void computeCoordinates(Hashtable hash)
    {
        String tmpString = new String("");
        Double tmpTF, tmpIDF;
        double TFIDFValue;
        Enumeration en = normTF.keys();
        while (en.hasMoreElements())
        {
            tmpString = (String) en.nextElement();
            tmpTF = (Double) normTF.get(tmpString);
            tmpIDF = (Double) hash.get(tmpString);
            if (tmpTF!=null && tmpIDF!=null)
            {
              TFIDFValue = (double) (tmpTF.doubleValue() * tmpIDF.doubleValue());
              coordinates.put(new String(tmpString), new Double(TFIDFValue));
            }
        }
    }


//---------------------------------- RANKING

    public void rankTerms(int firstNum)
    {
        rankTFIDF(firstNum);
    }//rankTerms

    public void rankTerms(int firstNum, int secondNum, double entropy, int numOfDocs, Category catObj, Hashtable category, String gm)
    {
        rankTFIDF(firstNum);
        rankGM(secondNum, entropy, numOfDocs, catObj, category, gm);
    }//rankTerms

    //Sort tokens according to descending TFIDF
    private void rankTFIDF(int num)
    {
        Hashtable hashCoord = new Hashtable();

        for (Enumeration e = coordinates.keys(); e.hasMoreElements(); )
        {
            String token = (String) e.nextElement();
            Double coord = (Double) coordinates.get(token);

            if (!hashCoord.containsKey(coord))
            {
                Vector tokenVec = new Vector();
                tokenVec.add(new String (token));
                hashCoord.put(new Double(coord.doubleValue()), tokenVec);
            }
            else
            {
                Vector tokenVec = (Vector) hashCoord.get(coord);
                tokenVec.add(new String (token));
                hashCoord.put(coord, tokenVec);
            }
        }

        Object [] ord = hashCoord.entrySet().toArray();
        TFIDFComparator tfidfComp = new TFIDFComparator();
        Arrays.sort(ord, (Comparator) tfidfComp);

        int bound;
        if (num <= ord.length)
            bound = num;
        else
            bound = ord.length;

        for (int i = 0; i < bound; i++)
        {
            Vector tokenVec = (Vector) ((Map.Entry) ord[i]).getValue();

            for (int j = 0; j < tokenVec.size(); j++)
            {
                if (j==num-1) break;
                String token = (String) tokenVec.elementAt(j);
            }
        }


    }//rankTFIDF


    //Sort tokens according to descending Goodness Measure (=GM)
    //GM => Information Gain, Mutual Information, Chi Square
    private void rankGM(int num, double entropy, int numOfDocs, Category catObj, Hashtable category, String gm)
    {
        Hashtable hashGM = new Hashtable();

        if (gm.equals("IG"))
        {
            for (Enumeration e = orderedVector.elements(); e.hasMoreElements(); )
            {
                String token = (String) e.nextElement();

                InfoGain igObj = new InfoGain(token, entropy, category);
                double ig = igObj.getInfoGain();
                hashGM.put(new String(token), new Double(ig));
            }
        }
        else if (gm.equals("CHI^2"))
        {
            for (Enumeration e = orderedVector.elements(); e.hasMoreElements(); )
            {
                String token = (String) e.nextElement();

                //----> Chi Square computation using average values

                ChiSquare chiSqObj = new ChiSquare(token, numOfDocs, category);
                double chiSq = chiSqObj.getChiSquare();
                hashGM.put(new String(token), new Double(chiSq));
            }
        }
        else if (gm.equals("MI"))
        {
            for (Enumeration e = orderedVector.elements(); e.hasMoreElements(); )
            {
                String token = (String) e.nextElement();

                //----> MI computation using average values

                MutualInfo miObj = new MutualInfo(token, numOfDocs, category);
                double mi = miObj.getMutualInfo();
                hashGM.put(new String(token), new Double(mi));
            }
        }
        
        qualityMeasures = hashGM;

        orderedVector.clear();

        Object [] ord = hashGM.entrySet().toArray();
        GMComparator gmComp = new GMComparator();
        Arrays.sort(ord, (Comparator) gmComp);

        int bound;
        if (num <= ord.length)
            bound = num;
        else
            bound = ord.length;

        for (int i=0; i<bound; i++)
        {
            String token = (String) ((Map.Entry) ord[i]).getKey();
            orderedVector.add(token);
        }
    }//rankGM



    public Vector getOrderedTokens()
    {
        return orderedVector;
    }

//---------------------------------- inner classes


    class TFIDFComparator implements Comparator
    {
        public int compare(Object o1, Object o2)
        {
            if (((Double) ((Map.Entry) o1).getKey()).doubleValue() < ((Double) ((Map.Entry) o2).getKey()).doubleValue())
                return (1);
            else if (((Double) ((Map.Entry) o1).getKey()).doubleValue() > ((Double) ((Map.Entry) o2).getKey()).doubleValue())
                return (-1);
            else
                return (0);
        }

    }//Inner Class TFIDFComparator.


    class GMComparator implements Comparator
    {
        public int compare(Object o1, Object o2)
        {
            if (((Double) ((Map.Entry) o1).getValue()).doubleValue() < ((Double) ((Map.Entry) o2).getValue()).doubleValue())
                return (1);
            else if (((Double) ((Map.Entry) o1).getValue()).doubleValue() > ((Double) ((Map.Entry) o2).getValue()).doubleValue())
                return (-1);
            else
            {
                String token1 = (String) ((Map.Entry) o1).getKey();
                String token2 = (String) ((Map.Entry) o2).getKey();
                if (token1.length() < token2.length())
                //if (token1.compareTo(token2) < 0)
                    return (-1);
                else if (token1.length() > token2.length())
                //else if (token1.compareTo(token2) > 0)
                    return (1);
                else
                    return (0);
            }
        }

    }//Inner Class GMComparator.


    class PairGM
    {
        private double GoodnessI;
        private double GoodnessII;

        public PairGM(double gmI, double gmII)
        {
            GoodnessI = gmI;
            GoodnessII = gmII;
        }

        public double getGoodnessI()
        {
            return GoodnessI;
        }

        public double getGoodnessII()
        {
            return GoodnessII;
        }

    }//Inner Class PairGM.

}//Class Document.
